package com.github.blanexie.nexusj.domain.peer

import cn.hutool.core.util.HexUtil
import com.github.blanexie.nexusj.support.AES
import java.nio.ByteBuffer
import java.time.LocalDateTime
import kotlin.experimental.and

data class PeerAggregate(
    val peerId: PeerId,

    var uploadedBytes: Size,
    var downloadedBytes: Size,
    var leftBytes: Size,
    var uploadHost: UploadHost,

    var event: String,
    var numwant: Int,

    var status: Int = 0,  //默认0 , 如果1 就是表示这个peerId被禁用了
    val createTime: LocalDateTime = LocalDateTime.now(),
    var uploadedTime: LocalDateTime = LocalDateTime.now(),
) {

    fun upload(
        uploadedBytes: Size,
        downloadedBytes: Size,
        leftBytes: Size,
        uploadHost: UploadHost,
        event: String,
        numwant: Int,
        uploadedTime: LocalDateTime
    ) {
        this.uploadedBytes = uploadedBytes
        this.downloadedBytes = downloadedBytes
        this.leftBytes = leftBytes
        this.uploadHost = uploadHost
        this.event = event
        this.numwant = numwant
        this.uploadedTime = uploadedTime
    }


}


data class PeerId(val peerId: String, val trackerid: String, val authKey: AuthKey, var id: Int? = null)

/**
 * 远方主机的ip地址
 */
data class UploadHost(
    val compact: Boolean,
    val ip: String,
    val ipv6: String,
    val port: Int,
) {

    fun compactPeer(): ByteArray {
        if (!compact) {
            return ByteArray(0)
        }
        val map = ip!!.split(".").map { it.toInt().toByte() }
        val p1 = port ushr 8
        val p2 = port and 255
        val plus = map.plus(p1.toByte()).plus(p2.toByte())
        return plus.toByteArray()
    }

    fun compactPeer6(ipv6: String, port: Int): ByteArray {
        if (!compact) {
            return ByteArray(0)
        }
        val ipv6Bytes = compactIpv6(ipv6)
        val p1 = port ushr 8
        val p2 = port and 255
        return ipv6Bytes.plus(p1.toByte()).plus(p2.toByte())
    }

    private fun compactIpv6(ipv6: String): ByteArray {
        if (ipv6.startsWith("::")) {
            val split = ipv6.split("::")
            val s2 = split[1]
            val decodeHex1 = HexUtil.decodeHex(s2)
            val i = 16 - -decodeHex1.size
            return ByteArray(i).plus(decodeHex1)
        }
        if (ipv6.endsWith("::")) {
            val split = ipv6.split("::")
            val s2 = split[0]
            val decodeHex1 = HexUtil.decodeHex(s2)
            val i = 16 - -decodeHex1.size
            return decodeHex1.plus(ByteArray(i))
        }
        if (ipv6.contains("::")) {
            val split = ipv6.split("::")
            val s1 = split[0]
            val s2 = split[1]
            val decodeHex = HexUtil.decodeHex(s1)
            val decodeHex1 = HexUtil.decodeHex(s2)
            val i = 16 - decodeHex.size - decodeHex1.size
            return decodeHex.plus(ByteArray(i)).plus(decodeHex1)
        }
        val split = ipv6.split(":")
        return split.map { HexUtil.decodeHex(it) }.reduce { acc, bytes -> acc.plus(bytes) }
    }

}


/**
 * 上报
 */
data class AuthKey(val authKey: String, val infoHash: String) {

    val userId: Int

    init {
        val decrypt = AES.decrypt(authKey)
        val decodeHex = HexUtil.decodeHex(infoHash)
        val byteBuffer = ByteBuffer.allocate(4)
        for (i in 0..3) {
            val byte = decrypt[i] and decodeHex[i]
            byteBuffer.put(byte)
        }
        for (i in 3..decodeHex.size) {
            assert(decrypt[i] == decodeHex[i])
        }
        this.userId = byteBuffer.int
    }
}


data class Size(val bytes: Long) {
    val k = 1024
    val m = 1024 * 1024.0
    val g = 1024 * 1024 * 1024.0
    val t = 1024 * 1024 * 1024 * 1024.0
    val p = 1024 * 1024 * 1024 * 1024 * 1024.0

    fun KB(): Long {
        return bytes / k
    }

    fun MB(): Double {
        val d = bytes / m
        val s = (d * 10).toLong()
        return s / 10.0
    }

    fun GB(): Double {
        val d = bytes / g
        val s = (d * 100).toLong()
        return s / 100.0
    }

    fun TB(): Double {
        val d = bytes / t
        val s = (d * 1000).toLong()
        return s / 1000.0
    }

    fun PB(): Double {
        val d = bytes / p
        val s = (d * 1000).toLong()
        return s / 1000.0
    }
}


